package imachine.engine;

import static org.junit.Assert.*;

import java.util.ArrayList;

import imachine.Machine;
import imachine.State;
import imachine.test.groupcall.ConnectedMachine;
import imachine.test.groupcall.GroupCallMachine;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class EngineTest {

	@Before
	public void setUp() throws Exception {
		testMachine = GroupCallMachine.getInstance();
		testEngine = new Engine(testMachine);
		testInContext = new String();
		testOutContext = new String();
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testEngineMachine() {
		int rtn = 0;
		
		////////////////////////
		//null param
		Engine engine = new Engine(null);
		assertTrue(engine.getMachine() == null);
		assertTrue(engine.getActiveStateId() == State.FINAL_STATE_ID);
		assertTrue(engine.getParent() == null);
		rtn = engine.start(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_INTERNAL);
		rtn = engine.shutdown(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_INTERNAL);
		rtn = engine.postEvent(GroupCallMachine.AFFILIATE_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_INTERNAL);

		//////////////////////////////
		//valid case
		engine = new Engine(GroupCallMachine.getInstance());
		assertTrue(engine.getMachine() == GroupCallMachine.getInstance());
		assertTrue(engine.getActiveStateId() == State.FINAL_STATE_ID);
		assertTrue(engine.getParent() == null);
		rtn = engine.start(testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
	}

	@Test
	public void testIsStarted() {
		assertTrue(!testEngine.isStarted());
		testEngine.start(testInContext, testOutContext);
		assertTrue(testEngine.isStarted());
	}

	@Test
	public void testStart() {
		int rtn = 0;
		assertTrue(!testEngine.isStarted());
		
		//successful start
		rtn = testEngine.start(testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		assertTrue(testEngine.isStarted());
		assertTrue(testEngine.getActiveStateId() == GroupCallMachine.ENTRY_STATE);
		
		//unsuccessful start
		rtn = testEngine.start(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_FORBIDDEN);
		assertTrue(testEngine.isStarted());
		assertTrue(testEngine.getActiveStateId() == GroupCallMachine.ENTRY_STATE);
		
		//start a sub engine
		Engine subEngine = testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE); 
		rtn = subEngine.start(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_FORBIDDEN);
		assertTrue(!subEngine.isStarted());
		assertTrue(subEngine.getActiveStateId() == State.FINAL_STATE_ID);
	}

	@Test
	public void testShutdown() {
		int rtn = 0;
		
		//successful shutdown
		assertTrue(!testEngine.isStarted());
		rtn = testEngine.start(testInContext, testOutContext);
		assertTrue(testEngine.isStarted());
		rtn = testEngine.shutdown(testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		assertTrue(!testEngine.isStarted());
		assertTrue(testEngine.getActiveStateId() == State.FINAL_STATE_ID);
		
		//unsuccessful shutdown
		rtn = testEngine.shutdown(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_FORBIDDEN);
		assertTrue(!testEngine.isStarted());
		assertTrue(testEngine.getActiveStateId() == State.FINAL_STATE_ID);

		//shutdown a sub engine
		Engine subEngine = testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE); 
		testEngine.start(testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.AFFILIATED_EVENT, testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.INVITE_EVENT, testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.INVITE_ACCEPTED_EVENT, testInContext, testOutContext);
		assertTrue(subEngine.isStarted());
		rtn = subEngine.shutdown(testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_FORBIDDEN);
		assertTrue(subEngine.isStarted());
	}

	@Test
	public void testPostEvent() {
		int stateId = 0;
		int rtn = 0;

		rtn = testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_FORBIDDEN);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == State.FINAL_STATE_ID);

		testEngine.start(testInContext, testOutContext);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.ENTRY_STATE);
		
		rtn = testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);

		rtn = testEngine.postEvent(GroupCallMachine.AFFILIATED_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATED_STATE);

		rtn = testEngine.postEvent(GroupCallMachine.INVITE_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.CONNECTING_STATE);
		
		rtn = testEngine.postEvent(GroupCallMachine.INVITE_ACCEPTED_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.CONNECTED_STATE);

		rtn = testEngine.postEvent(GroupCallMachine.DISCONNECT_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.DISCONNECTING_STATE);
		
		rtn = testEngine.postEvent(GroupCallMachine.DISCONNECTED_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATED_STATE);
		
		//invalid events
		rtn = testEngine.postEvent(GroupCallMachine.DISCONNECTED_EVENT, testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_INVALID_EVENT);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATED_STATE);
	}

	@Test
	public void testGetActiveStateId() {
		//before start
		assertTrue(!testEngine.isStarted());
		assertTrue(testEngine.getActiveStateId() == State.FINAL_STATE_ID);

		//after start
		testEngine.start(testInContext, testOutContext);
		assertTrue(testEngine.getActiveStateId() != State.FINAL_STATE_ID);

		//after shutdown
		testEngine.shutdown(testInContext, testOutContext);
		assertTrue(testEngine.getActiveStateId() == State.FINAL_STATE_ID);
	}

	@Test
	public void testGetMachine() {
		assertTrue(testEngine.getMachine() == testMachine);
		Engine subEngine = 
			testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE);
		assertTrue(subEngine.getMachine() == ConnectedMachine.getInstance());
	}
	
	@Test
	public void testGetParent() {
		Engine engine = null;
		assertTrue(testEngine.getParent() == null);
		engine = testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE);
		assertTrue(engine != null);
		assertTrue(engine.getParent() != null);
		assertTrue(engine.getParent().getMachine() == testMachine);
	}

	@Test
	public void testGetHistory() {
		Engine subEngine = testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE);
		ArrayList<Engine> history = new ArrayList<Engine>();
		
		//before start
		assertTrue(!testEngine.isStarted());
		testEngine.getHistory(history);
		assertTrue(history.size() == 0);
		subEngine.getHistory(history);
		assertTrue(history.size() == 0);
		
		//top level
		testEngine.start(testInContext, testOutContext);
		testEngine.getHistory(history);
		assertTrue(history.size() == 1);
		assertTrue(history.get(0) == testEngine);
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.AFFILIATED_EVENT, testInContext, testOutContext);
		testEngine.postEvent(GroupCallMachine.INVITE_EVENT, testInContext, testOutContext);
		testEngine.getHistory(history);
		assertTrue(history.size() == 1);
		assertTrue(history.get(0) == testEngine);
		subEngine.getHistory(history);
		assertTrue(history.size() == 0);

		//second level
		testEngine.postEvent(GroupCallMachine.INVITE_ACCEPTED_EVENT, testInContext, testOutContext);
		testEngine.getHistory(history);
		assertTrue(history.size() == 2);
		assertTrue(history.get(0) == testEngine);
		assertTrue(history.get(1) == subEngine);
		subEngine.getHistory(history);
		assertTrue(history.size() == 1);
		assertTrue(history.get(0) == subEngine);
		
		//after shutdown
		testEngine.shutdown(testInContext, testOutContext);
		testEngine.getHistory(history);
		assertTrue(history.size() == 0);
		subEngine.getHistory(history);
		assertTrue(history.size() == 0);
	}

	@Test
	public void testGetSubEngine() {
		//invalid state id
		Engine subEngine = null;
		subEngine = testEngine.getSubEngine(State.FINAL_STATE_ID);
		assertTrue(subEngine == null);
		
		//valid sub engine
		subEngine = testEngine.getSubEngine(GroupCallMachine.CONNECTED_STATE);
		assertTrue(subEngine != null);
		assertTrue(subEngine.getMachine() == ConnectedMachine.getInstance());
		
		//null sub engine
		subEngine = testEngine.getSubEngine(GroupCallMachine.AFFILIATED_STATE);
		assertTrue(subEngine == null);
	}

	@Test
	public void testAddMachineAction() {
		boolean setSuccessful = false;
		String inContext = "IN";
		String[] outContext = new String[1];
		
		//null listener action
		setSuccessful = testEngine.addMachineAction(null, "testAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addMachineAction(null, "testAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		
		//non-existent action
		setSuccessful = testEngine.addMachineAction(this, "nullAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addMachineAction(this, "nullAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		
		//non-public action
		setSuccessful = testEngine.addMachineAction(this, "privateAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addMachineAction(this, "privateAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		
		//invalid action
		setSuccessful = testEngine.addMachineAction(this, "invalidAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addMachineAction(this, "invalidAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		
		//valid action
		setSuccessful = testEngine.addMachineAction(this, "testAction", true);
		assertTrue(setSuccessful);
		setSuccessful = testEngine.addMachineAction(this, "testAction", false);
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		assertTrue(inContext.equals(outContext[0]));
		inContext = "FOO";
		outContext[0] = "";
		testEngine.shutdown(inContext, outContext);
		assertTrue(inContext.equals(outContext[0]));
		
		//add one more action
		setSuccessful = testEngine.addMachineAction(this, "testAction", true);
		assertTrue(setSuccessful);
		setSuccessful = testEngine.addMachineAction(this, "testAction", false);
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
	}

	@Test
	public void testRemoveMachineAction() {
		String inContext = "FOO";
		String[] outContext = new String[1];

		//add two actions
		inContext = "FOO";
		outContext[0] = "";
		testEngine.addMachineAction(this, "testAction", true);
		testEngine.addMachineAction(this, "testAction", true);
		testEngine.addMachineAction(this, "testAction", false);
		testEngine.addMachineAction(this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		
		//null listener
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeMachineAction(null, "testAction", true);
		testEngine.removeMachineAction(null, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		
		/////////////////////////////////////////
		//inexistent action
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeMachineAction(null, "nullAction", true);
		testEngine.removeMachineAction(null, "nullAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		
		/////////////////////////////////////////
		//remove actions
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeMachineAction(this, "testAction", true);
		testEngine.removeMachineAction(this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOO"));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));

		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeMachineAction(this, "testAction", true);
		testEngine.removeMachineAction(this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals(""));

		//do one more time event when all are gone
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeMachineAction(this, "testAction", true);
		testEngine.removeMachineAction(this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);
		assertTrue(outContext[0].equals(""));
	}

	@Test
	public void testAddStateAction() {
		boolean setSuccessful = false;
		String inContext = "IN";
		String[] outContext = new String[1];
		
		//null listener action
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, null, "testAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, null, "testAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//invalid state
		setSuccessful = testEngine.addStateAction(-1, this, "testAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(-1, this, "testAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//invalid state
		setSuccessful = testEngine.addStateAction(testEngine.getMachine().getStateNum(), this, "testAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(testEngine.getMachine().getStateNum(), this, "testAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//non-existent action
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "nullAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "nullAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//non-public action
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "privateAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "privateAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//invalid action
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "invalidAction", true);
		assertTrue(!setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "invalidAction", false);
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//valid action
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", true);
		assertTrue(setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", false);
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		assertTrue(inContext.equals(outContext[0]));
		inContext = "FOO";
		outContext[0] = "";
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//add one more
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", true);
		assertTrue(setSuccessful);
		setSuccessful = testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", false);
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		inContext = "FOO";
		outContext[0] = "";
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
	}

	@Test
	public void testRemoveStateAction() {
		String inContext = "FOO";
		String[] outContext = new String[1];

		////////////////////////////////////////
		//add two actions
		inContext = "FOO";
		outContext[0] = "";
		testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", true);
		testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", true);
		testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", false);
		testEngine.addStateAction(GroupCallMachine.ENTRY_STATE, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		/////////////////////////////////////////
		//null listener
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(GroupCallMachine.ENTRY_STATE, null, "testAction", true);
		testEngine.removeStateAction(GroupCallMachine.ENTRY_STATE, null, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		/////////////////////////////////////////
		//invalid state
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(State.FINAL_STATE_ID, this, "testAction", true);
		testEngine.removeStateAction(State.FINAL_STATE_ID, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		/////////////////////////////////////////
		//invalid state
		inContext = "FOO";
		outContext[0] = "";
		int stateId = testEngine.getMachine().getStateNum();
		testEngine.removeStateAction(stateId, this, "testAction", true);
		testEngine.removeStateAction(stateId, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		/////////////////////////////////////////
		//inexistent action
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "nullAction", true);
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "nullAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOOFOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		/////////////////////////////////////////
		//remove actions
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", true);
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals("FOO"));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", true);
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);		
		
		//do one more time event when all are gone
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", true);
		testEngine.removeStateAction(GroupCallMachine.AFFILIATE_EVENT, this, "testAction", false);
		testEngine.start(inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.postEvent(GroupCallMachine.AFFILIATE_EVENT, inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);		
	}

	@Test
	public void testAddTransitionAction() {
		boolean setSuccessful = false;
		String inContext = "IN";
		String[] outContext = new String[1];
		int tranId = GroupCallMachine.DISAFFILIATED_TO_AFFILIATING_TRAN;
		int eventId = GroupCallMachine.AFFILIATE_EVENT;
		
		//null listener action
		setSuccessful = testEngine.addTransitionAction(tranId, null, "testAction");
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//invalid transition
		setSuccessful = testEngine.addTransitionAction(-1, this, "testAction");
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//non-existent action
		setSuccessful = testEngine.addTransitionAction(tranId, this, "nullAction");
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//non-public action
		setSuccessful = testEngine.addTransitionAction(tranId, this, "privateAction");
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//invalid action
		setSuccessful = testEngine.addTransitionAction(tranId, this, "invalidAction");
		assertTrue(!setSuccessful);
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(!inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);

		//valid action
		setSuccessful = testEngine.addTransitionAction(tranId, this, "testAction");
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(inContext.equals(outContext[0]));
		testEngine.shutdown(inContext, outContext);
		
		//add one more action
		setSuccessful = testEngine.addTransitionAction(tranId, this, "testAction");
		assertTrue(setSuccessful);
		inContext = "FOO";
		outContext[0] = "";
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
	}

	@Test
	public void testRemoveTransitionAction() {
		String inContext = "IN";
		String[] outContext = new String[1];
		int tranId = GroupCallMachine.DISAFFILIATED_TO_AFFILIATING_TRAN;
		int eventId = GroupCallMachine.AFFILIATE_EVENT;
		
		inContext = "FOO";
		outContext[0] = "";
		testEngine.addTransitionAction(tranId, this, "testAction");
		testEngine.addTransitionAction(tranId, this, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		//null listener
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(tranId, null, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		//invalid transition id
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(-1, this, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		//inexistent action
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(tranId, this, "nullAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOOFOO"));
		testEngine.shutdown(inContext, outContext);
		
		//remove one action
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(tranId, this, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals("FOO"));
		testEngine.shutdown(inContext, outContext);
		
		//remove another action
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(tranId, this, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);
		
		//remove one more action event when all are actually gone
		inContext = "FOO";
		outContext[0] = "";
		testEngine.removeTransitionAction(tranId, this, "testAction");
		testEngine.start(inContext, outContext);
		testEngine.postEvent(eventId, inContext, outContext);
		assertTrue(outContext[0].equals(""));
		testEngine.shutdown(inContext, outContext);
	}

	@Test
	public void testSetGuard() {
		int stateId = 0;
		int rtn = 0;
		boolean setSuccessful = false;
		int tranId = GroupCallMachine.DISAFFILIATED_TO_AFFILIATING_TRAN;
		int eventId = GroupCallMachine.AFFILIATE_EVENT;

		testEngine.start(testInContext, testOutContext);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//null listener
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, 
					null, "falseGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//invalid transition id
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(-1, 
					null, "falseGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);

		//invalid transition id
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(testEngine.getMachine().getTransitionNum(), 
					null, "falseGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//non-existent guard
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, 
					this, "nullGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//non-public guard
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, 
					null, "privateGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//guard with invalid param
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, 
					this, "invalidGuard");
		assertTrue(!setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//true guard
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, this, "trueGuard");
		assertTrue(setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.OK);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.AFFILIATING_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
		//false guard
		testEngine.start(testInContext, testOutContext);
		setSuccessful = testEngine.setGuard(tranId, 
					this, "falseGuard");
		assertTrue(setSuccessful);
		rtn = testEngine.postEvent(eventId, testInContext, testOutContext);
		assertTrue(rtn == Engine.ERR_TRANSITION_FAILURE);
		stateId = testEngine.getActiveStateId();
		assertTrue(stateId == GroupCallMachine.ENTRY_STATE);
		testEngine.shutdown(testInContext, testOutContext);
		
	}

	@Test
	public void testSetDebug() {
		//fail("Not yet tested");
	}

	public Boolean invalidGuard(int transitionId, 
			final Object inContext, 
			Object outContext) {
		return false;
	}
	
	public Boolean trueGuard(Integer transitionId, 
			final Object inContext, 
			Object outContext) {
		return true;
	}

	private Boolean privateGuard(Integer transitionId, 
			final Object inContext, 
			Object outContext) {
		return false;
	}
	
	public Boolean falseGuard(Integer transitionId, 
			final Object inContext, 
			Object outContext) {
		return false;
	}
	
	public void testAction(Integer id, 
			final Object inContext, 
			Object outContext) {
		String inStr = String.class.cast(inContext);
		String[] out = String[].class.cast(outContext);
		out[0] += inStr;
	}
	
	private void privateAction(Integer id, 
			final Object inContext, 
			Object outContext) {
	}
	
	public void invalidAction(int id, 
			final Object inContext, 
			Object outContext) {
	}
	
	private Machine testMachine = null;
	private Engine testEngine = null;
	private String testInContext;
	private String testOutContext;
	
}
